#include <hd_led.h>
#include <hd_oled.h>
#include <pd_spi_hd.h>
#include <hd_oled_protocol_spi.h>
#include <pd_spi_sim.h>
#include <hd_w25qxx.h>
#include <pd_i2c_hd.h>
#include <hd_oled_protocol_i2c.h>
#include <hd_flash.h>
#include "main.h"

#define OLED_PROTOCOL       2   // Select a protocol. SPI, 1; I2C, 2;
#define OLED_PROTOCOL_IMPL  2   // Select a implement. simulate, 1; hardware, 2;
#define SPI_SIMULATE        1

static OLED *oled;

void oled_instance();

extern void SystemClock_Config(void);

void w25qxx_test(W25Qxx *w25qxx, const uint32_t addr, uint8_t *dat, uint32_t count, uint8_t y);

int main(void) {
    SystemClock_Config();

    NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);

    oled_instance();
    oled->showString(oled, 0, 0, "MID:    DID:");
    oled->showString(oled, 0, 16, "R:      W:");
    oled->showString(oled, 0, 32, "MID:    DID:");
    oled->showString(oled, 0, 48, "R:      W:");
    oled->refresh(oled);

    GPIO *spi1SselGPIO[] = {GPIOBuilder.build(GPIOA, LL_GPIO_PIN_2),   // OLED
                            GPIOBuilder.build(GPIOA, LL_GPIO_PIN_4),   // W25Q64 on chip
                            GPIOBuilder.build(GPIOC, LL_GPIO_PIN_4),}; // W25Q64 external
    GPIO *spi1SclkGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_5); // Clock, SPIx_SCK
    GPIO *spi1MisoGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_6); // MOSI,  SPIx_MOSI
    GPIO *spi1MosiGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_7); // MOSI,  SPIx_MOSI
    SPIGPIO *spi1GPIO = SPIGPIOBuilder.build(spi1SselGPIO, 3, spi1SclkGPIO, spi1MosiGPIO, spi1MisoGPIO);

#ifdef SPI_SIMULATE
    SPI *spi1 = (SPI *) SPISimulateBuilder.build(spi1GPIO);
    spi1->init(spi1);
#elif SPI_HARDWARE
    SPI *spi1 = (SPI *) SPIHardwareBuilder.build(SPI1, &spi1GPIO);
    spi1->init(spi1);
#endif

    W25Qxx *w25Qxx3OnChip = W25QxxBuilder.build(spi1, spi1SselGPIO[1]);
    w25Qxx3OnChip->init(w25Qxx3OnChip);

    W25Qxx *w25Qxx3Ext = W25QxxBuilder.build(spi1, spi1SselGPIO[2]);
    w25Qxx3Ext->init(w25Qxx3Ext);


    uint8_t mID;
    uint16_t dID;
    w25Qxx3OnChip->getId(w25Qxx3OnChip, &mID, &dID);
    oled->printf(oled, 32, 0, "%02X", mID);
    oled->printf(oled, 96, 0, "%04X", dID);

    w25Qxx3Ext->getId(w25Qxx3Ext, &mID, &dID);
    oled->printf(oled, 32, 32, "%02X", mID);
    oled->printf(oled, 96, 32, "%04X", dID);
    oled->refresh(oled);

    uint32_t ADDR = 0x001000;
    uint8_t array[] = {0xBA, 0xBA,};
    w25qxx_test(w25Qxx3OnChip, ADDR, array, sizeof(array) / sizeof(array[0]), 16);
    w25qxx_test(w25Qxx3Ext, ADDR, array, sizeof(array) / sizeof(array[0]), 48);

    while (1) {}
}

void w25qxx_test(W25Qxx *w25qxx, const uint32_t addr, uint8_t *dat, uint32_t count, uint8_t y) {
    // read
    w25qxx->read(w25qxx, addr, dat, count);
    for (int i = 0; i < count; ++i) {
        oled->printf(oled, 16 + i * 8 * 3, y, "%02X", dat[i]);
    }
    oled->refresh(oled);

    for (int i = 0; i < count; ++i) {
        dat[i] += 1;
    }

    // write
    w25qxx->write(w25qxx, addr, dat, count);

    // read
    w25qxx->read(w25qxx, addr, dat, count);
    for (int i = 0; i < count; ++i) {
        oled->printf(oled, 80 + i * 8 * 3, y, "%02X", dat[i]);
    }
    oled->refresh(oled);
}

void oled_instance() {
#if OLED_PROTOCOL == 1
    GPIO *sckGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_5); // Clock, SPIx_SCK
    GPIO *sdaGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_7); // MOSI,  SPIx_MOSI
    GPIO *resGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_3); // Reset, General IO, the chip reset pin is better
    GPIO *dcGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_1);  // Data or Command, General IO
    GPIO *csGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_2);  // Chip Select(Slave Select), General IO
    SPIGPIO *spiGPIO = SPIGPIOBuilder.build(&csGPIO, 1, sckGPIO, sdaGPIO, NULL);

#if OLED_PROTOCOL_IMPL == 1
    SPISimulate *spiObj = SPISimulateBuilder.build(spiGPIO);
#elif OLED_PROTOCOL_IMPL == 2
    SPIHardware *spiObj = SPIHardwareBuilder.build(SPI1, spiGPIO);
#endif
    SPI *spi1 = (SPI *) spiObj;
    spi1->init(spi1);
    OLEDProtocolSPI *oledProtocolObj = OLEDProtocolSPIBuilder.build(spi1, resGPIO, dcGPIO, csGPIO);

#elif OLED_PROTOCOL == 2
    GPIO *sckGPIO = GPIOBuilder.build(GPIOB, LL_GPIO_PIN_10);  // I2Cx_SCL
    GPIO *sdaGPIO = GPIOBuilder.build(GPIOB, LL_GPIO_PIN_11);  // I2Cx_SDA
#if OLED_PROTOCOL_IMPL == 1
    I2CSimulate *i2CObj = I2CSimulateBuilder.build(sckGPIO, sdaGPIO);
#elif OLED_PROTOCOL_IMPL == 2
    I2CHardware *i2CObj = I2CHardwareBuilder.build(I2C2, sckGPIO, sdaGPIO);
#endif
    I2C *i2c = (I2C *) i2CObj;
    i2c->init(i2c);
    OLEDProtocolI2C *oledProtocolObj = OLEDProtocolI2CBuilder.build(i2c);
#endif

    StorageFlash *storageFlash = StorageFlashBuilder.build();
    OLEDFont *oledFont = OLEDFontBuilder.build((Storage *) storageFlash, ENCODING_UTF8);
    oled = OLEDBuilder.build((OLEDProtocol *) oledProtocolObj, oledFont);
    oled->init(oled);
}



